/*******************************************************************
 *
 * Copyright (C), 2021-2022, Letstrong, All rights reserved.
 *
 * Author      : 
 * Create Time : 2021-01-04
 * Version     : v0.1
 * Description : 
 *******************************************************************/
 
/*----------------------------Head file----------------------------*/
#include "user_config.h"

#if(ROPE_CENTRAL_SUP_ENABLE)

#include "gap.h"
#include "gattc_task.h"
#include "central.h"
#include "central_task.h"
#include "prf_utils.h"
#include "ke_mem.h"
#include "co_utils.h"

/*----------------------------macro file---------------------------*/

/*----------------------------type define--------------------------*/

/*----------------------------var define---------------------------*/

struct attm_desc central_att_db[CENTRAL_IDX_NB] = 
{
	// Central Service Declaration
	[CENTRAL_IDX_SVC] = {ATT_DECL_PRIMARY_SERVICE, PERM(RD, ENABLE), 0, 0},

	[CENTRAL_IDX_WRITE_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
	[CENTRAL_IDX_WRITE_VAL] = {CENTRAL_SVC_WRITE_CHAR_UUID, PERM(WRITE_REQ, ENABLE), PERM(RI, ENABLE) , CENTRAL_TRANSFER_DATA_MAX_LEN *sizeof(uint8_t)},

	[CENTRAL_IDX_IND_CHAR] = {ATT_DECL_CHARACTERISTIC, PERM(RD, ENABLE), 0, 0},
	[CENTRAL_IDX_IND_VAL] = {CENTRAL_SVC_INDICATE_CHAR_UUID, PERM(IND, ENABLE), PERM(RI, ENABLE), CENTRAL_TRANSFER_DATA_MAX_LEN *sizeof(uint8_t)},
	[CENTRAL_IDX_IND_CFG] = {ATT_DESC_CLIENT_CHAR_CFG, PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE), 0, 0},
};

/*-------------------------func declaration------------------------*/

/*-----------------------------------------------------------------*/


/**
 ****************************************************************************************
 * @brief Initialization of the CENTRAL module.
 * This function performs all the initializations of the Profile module.
 *  - Creation of database (if it's a service)
 *  - Allocation of profile required memory
 *  - Initialization of task descriptor to register application
 *      - Task State array
 *      - Number of tasks
 *      - Default task handler
 *
 * @param[out]    env        Collector or Service allocated environment data.
 * @param[in|out] start_hdl  Service start handle (0 - dynamically allocated), only applies for services.
 * @param[in]     app_task   Application task number.
 * @param[in]     sec_lvl    Security level (AUTH, EKS and MI field of @see enum attm_value_perm_mask)
 * @param[in]     param      Configuration parameters of profile collector or service (32 bits aligned)
 *
 * @return status code to know if profile initialization succeed or not.
 ****************************************************************************************
 */
static uint8_t central_init(struct prf_task_env* env, uint16_t* start_hdl, uint16_t app_task, uint8_t sec_lvl, struct central_db_cfg* params)
{
	//------------------ create the attribute database for the profile -------------------
	// Service content flag
	uint32_t cfg_flag = CENTRAL_MANDATORY_MASK;
	// DB Creation Status
	uint8_t status = ATT_ERR_NO_ERROR;

	// Add service in the database
	status = attm_svc_create_db(start_hdl, CENTRAL_PRIMARY_SVC_UUID, (uint8_t *)&cfg_flag,
			CENTRAL_IDX_NB, NULL, env->task, &central_att_db[CENTRAL_IDX_SVC],
			(sec_lvl & (PERM_MASK_SVC_DIS | PERM_MASK_SVC_AUTH | PERM_MASK_SVC_EKS)));

	//And need to set req->sec_lvl = 0x40; //PERM_MASK_SVC_UUID_LEN 128 bit in app_user_add_sensor()
	
	//-------------------- allocate memory required for the profile  ---------------------
	if (status == ATT_ERR_NO_ERROR)
	{
		// Allocate USER required environment variable
		struct central_env_tag* central_env =
				(struct central_env_tag* ) ke_malloc(sizeof(struct central_env_tag), KE_MEM_ATT_DB);

		// Initialize USER environment
		env->env           = (prf_env_t*)central_env;
		central_env->start_hdl = *start_hdl;
		central_env->features = params->features;
		central_env->prfl_cfg = params->prfl_cfg;

		central_env->prf_env.app_task    = app_task
				| (PERM_GET(sec_lvl, SVC_MI) ? PERM(PRF_MI, ENABLE) : PERM(PRF_MI, DISABLE));
		// Multi Instantiated task
		central_env->prf_env.prf_task    = env->task | PERM(PRF_MI, ENABLE);

		// initialize environment variable
		env->id                     = TASK_ID_CENTRAL;
		env->desc.idx_max           = CENTRAL_SENSOR_IDX_MAX;
		env->desc.state             = central_env->state;
		env->desc.default_handler   = &central_default_handler;

		memset(central_env->ind_cfg, 0, BLE_CONNECTION_MAX);

		/* Put BLS in disabled state */
		ke_state_set(env->task, CENTRAL_IDLE);
	}

	return (status);
}

/**
 ****************************************************************************************
 * @brief Destruction of the CENTRAL module - due to a reset for instance.
 * This function clean-up allocated memory (attribute database is destroyed by another
 * procedure)
 *
 * @param[in|out]    env        Collector or Service allocated environment data.
 ****************************************************************************************
 */
static void central_destroy(struct prf_task_env* env)
{
	struct central_env_tag* central_env = (struct central_env_tag*) env->env;

	// free profile environment variables
	env->env = NULL;
	ke_free(central_env);
}

/**
 ****************************************************************************************
 * @brief Handles Connection creation
 *
 * @param[in|out]    env        Collector or Service allocated environment data.
 * @param[in]        conidx     Connection index
 ****************************************************************************************
 */
static void central_create(struct prf_task_env* env, uint8_t conidx)
{
	struct central_env_tag* central_env = (struct central_env_tag*) env->env;

	central_env->ind_cfg[conidx] = 0;

	/* Put BLS in idle state */
	ke_state_set(KE_BUILD_ID(env->task, conidx), CENTRAL_IDLE);
}

/**
 ****************************************************************************************
 * @brief Handles Disconnection
 *
 * @param[in|out]    env        Collector or Service allocated environment data.
 * @param[in]        conidx     Connection index
 * @param[in]        reason     Detach reason
 ****************************************************************************************
 */
static void central_cleanup(struct prf_task_env* env, uint8_t conidx, uint8_t reason)
{
	struct central_env_tag* central_env = (struct central_env_tag*) env->env;

	// clean-up environment variable allocated for task instance
	central_env->ind_cfg[conidx] = 0;
}


uint16_t central_get_att_handle(uint8_t att_idx)
{
	struct central_env_tag *central_env = PRF_ENV_GET(CENTRAL, central);
	uint16_t handle = ATT_INVALID_HDL;

	handle = central_env->start_hdl;

	// increment index according to expected index
	if(att_idx < CENTRAL_IDX_NB) {
		handle += att_idx;
	}
	else {
		handle = ATT_INVALID_HDL;
	}

	return handle;
}

void central_indicate_data(struct central_ind_upd_req const *param)
{
	struct central_env_tag* central_env = PRF_ENV_GET(CENTRAL, central);
	// Allocate the GATT notification message
	struct gattc_send_evt_cmd *send_lvl = KE_MSG_ALLOC_DYN(GATTC_SEND_EVT_CMD,
			KE_BUILD_ID(TASK_GATTC, 0), prf_src_task_get(&(central_env->prf_env),0),
			gattc_send_evt_cmd, sizeof(uint8_t)* CENTRAL_TRANSFER_DATA_MAX_LEN);

	// Fill in the parameter structure
	send_lvl->operation = GATTC_INDICATE;
	send_lvl->handle = central_get_att_handle(CENTRAL_IDX_IND_VAL);
	// pack measured value in database
	send_lvl->length = param->len;
	memcpy(send_lvl->value, param->value, param->len);
	// send notification to peer device
	ke_msg_send(send_lvl);
}

/*
 * GLOBAL VARIABLE DEFINITIONS
 ****************************************************************************************
 */

/// CENTRAL Task interface required by profile manager
const struct prf_task_cbs central_itf =
{
	(prf_init_fnct) central_init,
	central_destroy,
	central_create,
	central_cleanup,
};

/*
 * EXPORTED FUNCTIONS DEFINITIONS
 ****************************************************************************************
 */

const struct prf_task_cbs* central_prf_itf_get(void)
{
   return &central_itf;
}

#endif	//	if (ROPE_CENTRAL_SUP_ENABLE)

